package org.easystub;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


public class StubInvocationHandler implements InvocationHandler {

    private final InvocationReporter reporter;
    private final InvocationAnswerer defaultAnswerer;
    private final RecordedInvocationAnswerer recordedAnswerer;
    private final CaptureStore captureStore;
    private final Class<?> stubbedClass;

    public StubInvocationHandler(InvocationHandlerParameters params) {
        this.reporter = params.getReporter();
        this.recordedAnswerer = params.getRecordedAnswerer();
        this.captureStore = params.getCaptureStore();
        this.stubbedClass = params.getStubbedClass();
        this.defaultAnswerer = params.getDefaultAnswerer();
    }

    public Object invoke(Object o, Method method, Object[] methodArguments) throws Throwable {
        if ("toString".equals(method.getName())) {
            return proxyToString();
        }

        if ("hashCode".equals(method.getName())) {
            return 1;
        }

        if ("equals".equals(method.getName())) {
            return o == methodArguments[0];
        }

        Invocation invocation = new Invocation(o, method, methodArguments);

        reportInvocation(invocation);

        if(captureStore.isCapturingInvocation(invocation)) {
            captureStore.capture(invocation);
        }

        if (recordedAnswerer.canAnswer(invocation)) {
            return answer(invocation);
        } else {
            return defaultAnswerer.answer(invocation);
        }
    }

    private Object answer(Invocation invocation) throws Throwable {
        try {
            return recordedAnswerer.answer(invocation);
        } catch (ThrowableResultExceptionWrapper wrapper) {
            wrapper.rethrow();
        }
        //should never get here!
        return null;
    }

    private void reportInvocation(Invocation invocation) {
        reporter.report(invocation);
        GlobalInvocationReporter.report(invocation);
    }

    private Object proxyToString() {

        return String.format("<EasyStub proxy for %s> \n" +
                "Method invocations reported: \n%s", stubbedClass.getName(), recordedAnswerer);
    }
}
